﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Phone.Reactive;

namespace Liphofra.Core.EventAggregation
{
    /// <summary>
    /// An event aggregator implementation based on Rx Extensions.
    /// </summary>
    /// <remarks>
    /// Inspired by a blog post by Jose F. Romaniello:
    /// http://jfromaniello.blogspot.de/2010/04/event-aggregator-with-reactive.html
    /// Reworked for Windows Phone.</remarks>
    [SuppressMessage("StyleCop.CSharp.DocumentationRules", 
        "SA1650:ElementDocumentationMustBeSpelledCorrectly", 
        Justification = "Custom name and url pointing to a web site.")]
    public sealed class EventAggregator : IEventAggregator
    {
        private readonly IDictionary<Type, object> _subjects = new Dictionary<Type, object>();

        /// <summary>
        /// Gets the event so a consumer can subscribe to it.
        /// </summary>
        /// <typeparam name="TEvent">The type of the event to retrieve.</typeparam>
        /// <returns>
        /// An <see cref="IObservable{TEvent}" /> of the given event type that can be used to subscribe to the event.
        /// </returns>
        public IObservable<TEvent> GetEvent<TEvent>()
        {
            ISubject<TEvent> subject;
            var type = typeof(TEvent);

            lock (_subjects)
            {
                if (_subjects.ContainsKey(type))
                {
                    subject = (ISubject<TEvent>)_subjects[type];
                }
                else
                {
                    subject = new Subject<TEvent>();
                    _subjects[type] = subject;
                }
            }

            return subject.AsObservable();
        }

        /// <summary>
        /// Publishes the specified sample event.
        /// </summary>
        /// <typeparam name="TEvent">The type of the event.</typeparam>
        /// <param name="theEvent">The event instance to publish.</param>
        public void Publish<TEvent>(TEvent theEvent)
        {
            ISubject<TEvent> subject = null;
            var type = typeof(TEvent);

            lock (_subjects)
            {
                if (_subjects.ContainsKey(type))
                {
                    subject = (ISubject<TEvent>)_subjects[type];
                }
            }

            if (subject != null)
            {
                subject.OnNext(theEvent);
            }
        }
    }
}